{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<font color=\"red\">注</font>: 使用 tensorboard 可视化需要安装 tensorflow (TensorBoard依赖于tensorflow库，可以任意安装tensorflow的gpu/cpu版本)\n",
    "\n",
    "```shell\n",
    "pip install tensorflow-cpu\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-21T08:01:42.185013Z",
     "start_time": "2025-01-21T08:01:42.181175Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-03T06:24:20.025176Z",
     "iopub.status.busy": "2025-02-03T06:24:20.024531Z",
     "iopub.status.idle": "2025-02-03T06:24:26.716131Z",
     "shell.execute_reply": "2025-02-03T06:24:26.715292Z",
     "shell.execute_reply.started": "2025-02-03T06:24:20.025134Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/usr/local/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
      "  from .autonotebook import tqdm as notebook_tqdm\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "sys.version_info(major=3, minor=10, micro=14, releaselevel='final', serial=0)\n",
      "matplotlib 3.10.0\n",
      "numpy 1.26.4\n",
      "pandas 2.2.3\n",
      "sklearn 1.6.0\n",
      "torch 2.5.1+cu124\n",
      "cuda:0\n"
     ]
    }
   ],
   "source": [
    "import matplotlib as mpl\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "import numpy as np\n",
    "import sklearn\n",
    "import pandas as pd\n",
    "import os\n",
    "import sys\n",
    "import time\n",
    "from tqdm.auto import tqdm\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "\n",
    "print(sys.version_info)\n",
    "for module in mpl, np, pd, sklearn, torch:\n",
    "    print(module.__name__, module.__version__)\n",
    "    \n",
    "device = torch.device(\"cuda:0\") if torch.cuda.is_available() else torch.device(\"cpu\")\n",
    "print(device)\n",
    "\n",
    "seed = 42\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 数据准备\n",
    "\n",
    "https://www.kaggle.com/competitions/cifar-10/data\n",
    "\n",
    "```shell\n",
    "$ tree -L 1 cifar-10                                    \n",
    "cifar-10\n",
    "├── sampleSubmission.csv\n",
    "├── test\n",
    "├── train\n",
    "└── trainLabels.csv\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-21T08:01:43.186193Z",
     "start_time": "2025-01-21T08:01:42.198531Z"
    },
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-02-03T06:24:33.704822Z",
     "iopub.status.busy": "2025-02-03T06:24:33.704003Z",
     "iopub.status.idle": "2025-02-03T06:24:36.900327Z",
     "shell.execute_reply": "2025-02-03T06:24:36.899616Z",
     "shell.execute_reply.started": "2025-02-03T06:24:33.704784Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[(PosixPath('competitions/cifar-10/train/1.png'), 'frog'),\n",
      " (PosixPath('competitions/cifar-10/train/2.png'), 'truck'),\n",
      " (PosixPath('competitions/cifar-10/train/3.png'), 'truck'),\n",
      " (PosixPath('competitions/cifar-10/train/4.png'), 'deer'),\n",
      " (PosixPath('competitions/cifar-10/train/5.png'), 'automobile')]\n",
      "[(PosixPath('competitions/cifar-10/test/1.png'), 'cat'),\n",
      " (PosixPath('competitions/cifar-10/test/2.png'), 'cat'),\n",
      " (PosixPath('competitions/cifar-10/test/3.png'), 'cat'),\n",
      " (PosixPath('competitions/cifar-10/test/4.png'), 'cat'),\n",
      " (PosixPath('competitions/cifar-10/test/5.png'), 'cat')]\n",
      "50000 300000\n"
     ]
    }
   ],
   "source": [
    "from pathlib import Path\n",
    "\n",
    "DATA_DIR = Path(\".\")\n",
    "DATA_DIR1 =Path(\"competitions/cifar-10/\")\n",
    "train_lables_file = DATA_DIR / \"trainLabels.csv\"\n",
    "test_csv_file = DATA_DIR / \"sampleSubmission.csv\" #测试集模板csv文件\n",
    "train_folder = DATA_DIR1 / \"train\"\n",
    "test_folder = DATA_DIR1 / \"test\"\n",
    "\n",
    "#所有的类别\n",
    "class_names = [\n",
    "    'airplane',\n",
    "    'automobile',\n",
    "    'bird',\n",
    "    'cat',\n",
    "    'deer',\n",
    "    'dog',\n",
    "    'frog',\n",
    "    'horse',\n",
    "    'ship',\n",
    "    'truck',\n",
    "]\n",
    "\n",
    "def parse_csv_file(filepath, folder):\n",
    "    \"\"\"Parses csv files into (filename(path), label) format\"\"\"\n",
    "    results = []\n",
    "    #读取所有行\n",
    "    with open(filepath, 'r') as f:\n",
    "#         lines = f.readlines()  为什么加[1:]，可以试这个\n",
    "        #第一行不需要，因为第一行是标签\n",
    "        lines = f.readlines()[1:] \n",
    "    for line in lines:#依次去取每一行\n",
    "        image_id, label_str = line.strip('\\n').split(',')\n",
    "        image_full_path = folder / f\"{image_id}.png\"\n",
    "        results.append((image_full_path, label_str)) #得到对应图片的路径和分类\n",
    "    return results\n",
    "\n",
    "#解析对应的文件夹\n",
    "train_labels_info = parse_csv_file(train_lables_file, train_folder)\n",
    "test_csv_info = parse_csv_file(test_csv_file, test_folder)\n",
    "#打印\n",
    "import pprint\n",
    "pprint.pprint(train_labels_info[0:5])\n",
    "pprint.pprint(test_csv_info[0:5])\n",
    "print(len(train_labels_info), len(test_csv_info))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-21T08:01:43.217782Z",
     "start_time": "2025-01-21T08:01:43.186193Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-03T06:24:42.560602Z",
     "iopub.status.busy": "2025-02-03T06:24:42.559463Z",
     "iopub.status.idle": "2025-02-03T06:24:42.660253Z",
     "shell.execute_reply": "2025-02-03T06:24:42.659388Z",
     "shell.execute_reply.started": "2025-02-03T06:24:42.560549Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "                            filepath       class\n",
      "0  competitions/cifar-10/train/1.png        frog\n",
      "1  competitions/cifar-10/train/2.png       truck\n",
      "2  competitions/cifar-10/train/3.png       truck\n",
      "3  competitions/cifar-10/train/4.png        deer\n",
      "4  competitions/cifar-10/train/5.png  automobile\n",
      "                                filepath       class\n",
      "0  competitions/cifar-10/train/45001.png       horse\n",
      "1  competitions/cifar-10/train/45002.png  automobile\n",
      "2  competitions/cifar-10/train/45003.png        deer\n",
      "3  competitions/cifar-10/train/45004.png  automobile\n",
      "4  competitions/cifar-10/train/45005.png    airplane\n",
      "                           filepath class\n",
      "0  competitions/cifar-10/test/1.png   cat\n",
      "1  competitions/cifar-10/test/2.png   cat\n",
      "2  competitions/cifar-10/test/3.png   cat\n",
      "3  competitions/cifar-10/test/4.png   cat\n",
      "4  competitions/cifar-10/test/5.png   cat\n"
     ]
    }
   ],
   "source": [
    "# train_df = pd.DataFrame(train_labels_info)\n",
    "train_df = pd.DataFrame(train_labels_info[0:45000])\n",
    "valid_df = pd.DataFrame(train_labels_info[45000:])\n",
    "test_df = pd.DataFrame(test_csv_info)\n",
    "\n",
    "train_df.columns = ['filepath', 'class']\n",
    "valid_df.columns = ['filepath', 'class']\n",
    "test_df.columns = ['filepath', 'class']\n",
    "\n",
    "print(train_df.head())\n",
    "print(valid_df.head())\n",
    "print(test_df.head())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-21T08:01:44.158498Z",
     "start_time": "2025-01-21T08:01:43.217782Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-03T06:24:42.948640Z",
     "iopub.status.busy": "2025-02-03T06:24:42.948005Z",
     "iopub.status.idle": "2025-02-03T06:24:45.527463Z",
     "shell.execute_reply": "2025-02-03T06:24:45.526633Z",
     "shell.execute_reply.started": "2025-02-03T06:24:42.948606Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "from PIL import Image\n",
    "from torch.utils.data import Dataset, DataLoader\n",
    "from torchvision import transforms\n",
    "\n",
    "class Cifar10Dataset(Dataset):\n",
    "    df_map = {\n",
    "        \"train\": train_df,\n",
    "        \"eval\": valid_df,\n",
    "        \"test\": test_df\n",
    "    }\n",
    "    label_to_idx = {label: idx for idx, label in enumerate(class_names)}\n",
    "    idx_to_label = {idx: label for idx, label in enumerate(class_names)}\n",
    "    def __init__(self, mode, transform=None):\n",
    "        self.df = self.df_map.get(mode, None)\n",
    "        if self.df is None:\n",
    "            raise ValueError(\"mode should be one of train, val, test, but got {}\".format(mode))\n",
    "\n",
    "        self.transform = transform\n",
    "        \n",
    "    def __getitem__(self, index):\n",
    "        img_path, label = self.df.iloc[index]\n",
    "        img = Image.open(img_path).convert('RGB')\n",
    "        # # img 转换为 channel first\n",
    "        # img = img.transpose((2, 0, 1))\n",
    "        # transform\n",
    "        img = self.transform(img)\n",
    "        # label 转换为 idx\n",
    "        label = self.label_to_idx[label]\n",
    "        return img, label\n",
    "    \n",
    "    def __len__(self):\n",
    "        return self.df.shape[0]\n",
    "    \n",
    "IMAGE_SIZE = 32\n",
    "mean, std = [0.4914, 0.4822, 0.4465], [0.247, 0.243, 0.261]\n",
    "\n",
    "transforms_train = transforms.Compose([\n",
    "        # resize\n",
    "        transforms.Resize((IMAGE_SIZE, IMAGE_SIZE)),\n",
    "        # random rotation 40\n",
    "        transforms.RandomRotation(40),\n",
    "        # horizaontal flip\n",
    "        transforms.RandomHorizontalFlip(),\n",
    "        transforms.ToTensor(),\n",
    "        transforms.Normalize(mean, std)\n",
    "    ])\n",
    "\n",
    "transforms_eval = transforms.Compose([\n",
    "        # resize\n",
    "        transforms.Resize((IMAGE_SIZE, IMAGE_SIZE)),\n",
    "        transforms.ToTensor(),\n",
    "        transforms.Normalize(mean, std)\n",
    "    ])\n",
    "\n",
    "train_ds = Cifar10Dataset(\"train\", transforms_train)\n",
    "eval_ds = Cifar10Dataset(\"eval\", transforms_eval) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-21T08:01:44.162011Z",
     "start_time": "2025-01-21T08:01:44.158498Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-03T06:24:50.755150Z",
     "iopub.status.busy": "2025-02-03T06:24:50.754278Z",
     "iopub.status.idle": "2025-02-03T06:24:50.759858Z",
     "shell.execute_reply": "2025-02-03T06:24:50.759075Z",
     "shell.execute_reply.started": "2025-02-03T06:24:50.755113Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "batch_size = 64\n",
    "train_dl = DataLoader(train_ds, batch_size=batch_size, shuffle=True, num_workers=4)   \n",
    "eval_dl = DataLoader(eval_ds, batch_size=batch_size, shuffle=False, num_workers=4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-21T08:01:44.173034Z",
     "start_time": "2025-01-21T08:01:44.162011Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-03T06:24:51.298912Z",
     "iopub.status.busy": "2025-02-03T06:24:51.298366Z",
     "iopub.status.idle": "2025-02-03T06:24:51.302790Z",
     "shell.execute_reply": "2025-02-03T06:24:51.301995Z",
     "shell.execute_reply.started": "2025-02-03T06:24:51.298878Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# 遍历train_ds得到每张图片，计算每个通道的均值和方差\n",
    "# def cal_mean_std(ds):\n",
    "#     mean = 0.\n",
    "#     std = 0.\n",
    "#     for img, _ in ds:\n",
    "#         mean += img.mean(dim=(1, 2))\n",
    "#         std += img.std(dim=(1, 2))\n",
    "#     mean /= len(ds)\n",
    "#     std /= len(ds)\n",
    "#     return mean, std\n",
    "#\n",
    "# # 经过 normalize 后 均值为0，方差为1\n",
    "# print(cal_mean_std(train_ds))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 定义模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-21T08:07:02.978303Z",
     "start_time": "2025-01-21T08:07:02.972820Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-03T06:24:52.835868Z",
     "iopub.status.busy": "2025-02-03T06:24:52.835333Z",
     "iopub.status.idle": "2025-02-03T06:24:52.845127Z",
     "shell.execute_reply": "2025-02-03T06:24:52.844233Z",
     "shell.execute_reply.started": "2025-02-03T06:24:52.835832Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "class InceptionBolck(nn.Module):\n",
    "    #output_channel_for_each_path: 1*1卷积输出通道数，3*3卷积输出通道数，5*5卷积输出通道数，池化层输出通道数\n",
    "    def __init__(self, input_channels: int, output_channel_for_each_path: list[int]):\n",
    "        super(InceptionBolck, self).__init__()\n",
    "        self.conv1_1 = nn.Conv2d(\n",
    "            in_channels=input_channels, \n",
    "            out_channels=output_channel_for_each_path[0],\n",
    "            kernel_size=1, \n",
    "            padding=\"same\"\n",
    "            )\n",
    "        \n",
    "        self.conv3_3 = nn.Conv2d(\n",
    "            in_channels=input_channels, \n",
    "            out_channels=output_channel_for_each_path[1], \n",
    "            kernel_size=3, \n",
    "            padding=\"same\"\n",
    "            )\n",
    "        self.conv5_5 = nn.Conv2d(\n",
    "            in_channels=input_channels, \n",
    "            out_channels=output_channel_for_each_path[2], \n",
    "            kernel_size=5, \n",
    "            padding=\"same\"\n",
    "            ) # 5*5卷积核，要same，上下左右各补两圈0\n",
    "        self.max_pooling = nn.MaxPool2d(\n",
    "            kernel_size=2, \n",
    "            stride=2, \n",
    "            )\n",
    "        \n",
    "    def forward(self, x):\n",
    "        conv1_1 = F.relu(self.conv1_1(x))\n",
    "        conv3_3 = F.relu(self.conv3_3(x))\n",
    "        conv5_5 = F.relu(self.conv5_5(x))\n",
    "        max_pooling = self.max_pooling(x) # 最大池化，图像尺寸缩小一半\n",
    "        \n",
    "        max_pooling_shape = max_pooling.shape[1:] #最后3个维度，即通道、高、宽\n",
    "        input_shape = x.shape[1:] # 输入的通道、高、宽\n",
    "        width_padding = (input_shape[-2] - max_pooling_shape[-2]) // 2 # // 输入尺寸，减去池化尺寸，再除以2\n",
    "        height_padding = (input_shape[-1] - max_pooling_shape[-1]) // 2\n",
    "        padded_pooling = F.pad(\n",
    "            max_pooling, \n",
    "            [width_padding, width_padding, height_padding, height_padding]\n",
    "            ) # [left, right, top, bottom]，在每个维度上填充的长度，默认填充最后两个维度，即高和宽，填充0\n",
    "        concat_output = torch.cat(\n",
    "            [conv1_1, conv3_3, conv5_5, padded_pooling], \n",
    "            dim=1\n",
    "            ) # 在通道维度上拼接\n",
    "        return concat_output\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-02-03T07:00:52.436018Z",
     "iopub.status.busy": "2025-02-03T07:00:52.435165Z",
     "iopub.status.idle": "2025-02-03T07:00:52.441612Z",
     "shell.execute_reply": "2025-02-03T07:00:52.440679Z",
     "shell.execute_reply.started": "2025-02-03T07:00:52.435981Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "14592"
      ]
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "912*4*4"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-21T08:15:10.358796Z",
     "start_time": "2025-01-21T08:15:10.350873Z"
    },
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-02-03T07:01:02.286194Z",
     "iopub.status.busy": "2025-02-03T07:01:02.285687Z",
     "iopub.status.idle": "2025-02-03T07:01:02.362201Z",
     "shell.execute_reply": "2025-02-03T07:01:02.361458Z",
     "shell.execute_reply.started": "2025-02-03T07:01:02.286160Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "             model.0.weight             paramerters num: 864\n",
      "              model.0.bias              paramerters num: 32\n",
      "         model.3.conv1_1.weight         paramerters num: 1024\n",
      "          model.3.conv1_1.bias          paramerters num: 32\n",
      "         model.3.conv3_3.weight         paramerters num: 18432\n",
      "          model.3.conv3_3.bias          paramerters num: 64\n",
      "         model.3.conv5_5.weight         paramerters num: 12800\n",
      "          model.3.conv5_5.bias          paramerters num: 16\n",
      "         model.4.conv1_1.weight         paramerters num: 9216\n",
      "          model.4.conv1_1.bias          paramerters num: 64\n",
      "         model.4.conv3_3.weight         paramerters num: 124416\n",
      "          model.4.conv3_3.bias          paramerters num: 96\n",
      "         model.4.conv5_5.weight         paramerters num: 172800\n",
      "          model.4.conv5_5.bias          paramerters num: 48\n",
      "         model.6.conv1_1.weight         paramerters num: 33792\n",
      "          model.6.conv1_1.bias          paramerters num: 96\n",
      "         model.6.conv3_3.weight         paramerters num: 405504\n",
      "          model.6.conv3_3.bias          paramerters num: 128\n",
      "         model.6.conv5_5.weight         paramerters num: 704000\n",
      "          model.6.conv5_5.bias          paramerters num: 80\n",
      "         model.7.conv1_1.weight         paramerters num: 52480\n",
      "          model.7.conv1_1.bias          paramerters num: 80\n",
      "         model.7.conv3_3.weight         paramerters num: 661248\n",
      "          model.7.conv3_3.bias          paramerters num: 112\n",
      "         model.7.conv5_5.weight         paramerters num: 1049600\n",
      "          model.7.conv5_5.bias          paramerters num: 64\n",
      "            model.10.weight             paramerters num: 145920\n",
      "             model.10.bias              paramerters num: 10\n"
     ]
    }
   ],
   "source": [
    "class InceptionNet(nn.Module):\n",
    "    def __init__(self, num_classes=10):\n",
    "        super(InceptionNet, self).__init__()\n",
    "        self.model = nn.Sequential(\n",
    "            nn.Conv2d(in_channels=3, out_channels=32, kernel_size=3, stride=1, padding=\"same\"),\n",
    "            nn.ReLU(),\n",
    "            nn.MaxPool2d(kernel_size=2, stride=2),#16*16\n",
    "            InceptionBolck(input_channels=32, output_channel_for_each_path=[32, 64, 16]),#88*16*16\n",
    "            InceptionBolck(input_channels=144, output_channel_for_each_path=[64, 96, 48]),#168*16*16\n",
    "            nn.MaxPool2d(kernel_size=2, stride=2),#8*8\n",
    "            InceptionBolck(input_channels=352, output_channel_for_each_path=[96, 128, 80]),#272*8*8\n",
    "            InceptionBolck(input_channels=656, output_channel_for_each_path=[80, 112, 64]),#320*8*8\n",
    "            nn.MaxPool2d(kernel_size=2, stride=2),#2320*4*4\n",
    "            nn.Flatten(),\n",
    "            nn.Linear(14592, num_classes)\n",
    "        )\n",
    "        self.init_weights()\n",
    "        \n",
    "    def init_weights(self):\n",
    "        \"\"\"使用 xavier 均匀分布来初始化全连接层、卷积层的权重 W\"\"\"\n",
    "        for m in self.modules():\n",
    "            if isinstance(m, (nn.Linear, nn.Conv2d)):\n",
    "                nn.init.xavier_uniform_(m.weight)\n",
    "                nn.init.zeros_(m.bias)\n",
    "                \n",
    "    def forward(self, x):\n",
    "        return self.model(x)\n",
    "\n",
    "            \n",
    "for key, value in InceptionNet(len(class_names)).named_parameters():\n",
    "    print(f\"{key:^40}paramerters num: {np.prod(value.shape)}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-25T02:18:21.466720300Z",
     "start_time": "2024-07-25T02:18:21.440027700Z"
    },
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2025-02-03T07:01:05.323119Z",
     "iopub.status.busy": "2025-02-03T07:01:05.322580Z",
     "iopub.status.idle": "2025-02-03T07:01:05.380186Z",
     "shell.execute_reply": "2025-02-03T07:01:05.379389Z",
     "shell.execute_reply.started": "2025-02-03T07:01:05.323085Z"
    },
    "jupyter": {
     "outputs_hidden": false
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Total params: 3,393,018\n"
     ]
    }
   ],
   "source": [
    "#计算总参数量\n",
    "total_params = sum(p.numel() for p in InceptionNet(len(class_names)).parameters() if p.requires_grad)\n",
    "print(f\"Total params: {total_params:,}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-26T01:31:06.814529600Z",
     "start_time": "2024-07-26T01:31:06.152598300Z"
    },
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "from torchvision.models import inception_v3\n",
    "model=inception_v3()\n",
    "model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-26T01:33:28.411511600Z",
     "start_time": "2024-07-26T01:33:28.399487300Z"
    },
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2025-02-03T06:25:14.108620Z",
     "iopub.status.busy": "2025-02-03T06:25:14.107959Z",
     "iopub.status.idle": "2025-02-03T06:25:14.115143Z",
     "shell.execute_reply": "2025-02-03T06:25:14.114271Z",
     "shell.execute_reply.started": "2025-02-03T06:25:14.108583Z"
    },
    "jupyter": {
     "outputs_hidden": false
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Total params: 27,161,264\n"
     ]
    }
   ],
   "source": [
    "total_params = sum(p.numel() for p in model.parameters() if p.requires_grad)\n",
    "print(f\"Total params: {total_params:,}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-26T03:10:23.045400900Z",
     "start_time": "2024-07-26T03:10:21.357935400Z"
    },
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2025-02-03T06:25:14.686380Z",
     "iopub.status.busy": "2025-02-03T06:25:14.685724Z",
     "iopub.status.idle": "2025-02-03T06:25:47.361434Z",
     "shell.execute_reply": "2025-02-03T06:25:47.360665Z",
     "shell.execute_reply.started": "2025-02-03T06:25:14.686335Z"
    },
    "jupyter": {
     "outputs_hidden": false
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/usr/local/lib/python3.10/site-packages/torchvision/models/_utils.py:208: UserWarning: The parameter 'pretrained' is deprecated since 0.13 and may be removed in the future, please use 'weights' instead.\n",
      "  warnings.warn(\n",
      "/usr/local/lib/python3.10/site-packages/torchvision/models/_utils.py:223: UserWarning: Arguments other than a weight enum or `None` for 'weights' are deprecated since 0.13 and may be removed in the future. The current behavior is equivalent to passing `weights=VGG19_Weights.IMAGENET1K_V1`. You can also use `weights=VGG19_Weights.DEFAULT` to get the most up-to-date weights.\n",
      "  warnings.warn(msg)\n",
      "Downloading: \"https://download.pytorch.org/models/vgg19-dcbb9e9d.pth\" to /root/.cache/torch/hub/checkpoints/vgg19-dcbb9e9d.pth\n",
      "100%|██████████| 548M/548M [00:29<00:00, 19.3MB/s] \n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "VGG(\n",
       "  (features): Sequential(\n",
       "    (0): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
       "    (1): ReLU(inplace=True)\n",
       "    (2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
       "    (3): ReLU(inplace=True)\n",
       "    (4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n",
       "    (5): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
       "    (6): ReLU(inplace=True)\n",
       "    (7): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
       "    (8): ReLU(inplace=True)\n",
       "    (9): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n",
       "    (10): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
       "    (11): ReLU(inplace=True)\n",
       "    (12): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
       "    (13): ReLU(inplace=True)\n",
       "    (14): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
       "    (15): ReLU(inplace=True)\n",
       "    (16): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
       "    (17): ReLU(inplace=True)\n",
       "    (18): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n",
       "    (19): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
       "    (20): ReLU(inplace=True)\n",
       "    (21): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
       "    (22): ReLU(inplace=True)\n",
       "    (23): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
       "    (24): ReLU(inplace=True)\n",
       "    (25): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
       "    (26): ReLU(inplace=True)\n",
       "    (27): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n",
       "    (28): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
       "    (29): ReLU(inplace=True)\n",
       "    (30): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
       "    (31): ReLU(inplace=True)\n",
       "    (32): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
       "    (33): ReLU(inplace=True)\n",
       "    (34): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
       "    (35): ReLU(inplace=True)\n",
       "    (36): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n",
       "  )\n",
       "  (avgpool): AdaptiveAvgPool2d(output_size=(7, 7))\n",
       "  (classifier): Sequential(\n",
       "    (0): Linear(in_features=25088, out_features=4096, bias=True)\n",
       "    (1): ReLU(inplace=True)\n",
       "    (2): Dropout(p=0.5, inplace=False)\n",
       "    (3): Linear(in_features=4096, out_features=4096, bias=True)\n",
       "    (4): ReLU(inplace=True)\n",
       "    (5): Dropout(p=0.5, inplace=False)\n",
       "    (6): Linear(in_features=4096, out_features=1000, bias=True)\n",
       "  )\n",
       ")"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from torchvision.models import vgg19\n",
    "model=vgg19(pretrained=True)\n",
    "model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-24T03:36:24.264691400Z",
     "start_time": "2024-07-24T03:36:24.252349Z"
    },
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2025-02-03T06:25:52.111655Z",
     "iopub.status.busy": "2025-02-03T06:25:52.110929Z",
     "iopub.status.idle": "2025-02-03T06:25:52.154698Z",
     "shell.execute_reply": "2025-02-03T06:25:52.153955Z",
     "shell.execute_reply.started": "2025-02-03T06:25:52.111612Z"
    },
    "jupyter": {
     "outputs_hidden": false
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[[[ 0.0000,  0.0000,  0.0000,  0.0000,  0.0000,  0.0000,  0.0000,\n",
       "            0.0000],\n",
       "          [ 0.0000,  0.0000,  0.0000,  0.0000,  0.0000,  0.0000,  0.0000,\n",
       "            0.0000],\n",
       "          [ 0.0000,  0.0000, -0.4559, -2.1586, -0.4394, -0.8597,  0.0000,\n",
       "            0.0000],\n",
       "          [ 0.0000,  0.0000, -1.0323, -0.8406, -0.5150,  1.0348,  0.0000,\n",
       "            0.0000],\n",
       "          [ 0.0000,  0.0000,  0.8508,  0.0917,  0.7913,  1.5040,  0.0000,\n",
       "            0.0000],\n",
       "          [ 0.0000,  0.0000,  0.1798,  0.8668, -0.1257,  0.6785,  0.0000,\n",
       "            0.0000],\n",
       "          [ 0.0000,  0.0000,  0.0000,  0.0000,  0.0000,  0.0000,  0.0000,\n",
       "            0.0000],\n",
       "          [ 0.0000,  0.0000,  0.0000,  0.0000,  0.0000,  0.0000,  0.0000,\n",
       "            0.0000]]]])"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#给F.pad写一个例子\n",
    "input_shape = (3, 8, 8)\n",
    "max_pooling_shape = (4, 4)\n",
    "width_padding = (8 - 4) // 2\n",
    "height_padding = (8 - 4) // 2\n",
    "padded_pooling = F.pad(\n",
    "    torch.randn(1, 1,4,4),\n",
    "    [width_padding, width_padding, height_padding, height_padding]\n",
    "    ) # [left, right, top, bottom]，在每个维度上填充的长度，默认填充最后两个维度，即高和宽，填充0\n",
    "padded_pooling"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "jp-MarkdownHeadingCollapsed": true,
    "tags": []
   },
   "source": [
    "## 定义损失函数和优化器"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2025-02-03T06:25:56.302998Z",
     "iopub.status.busy": "2025-02-03T06:25:56.302141Z",
     "iopub.status.idle": "2025-02-03T06:25:56.306717Z",
     "shell.execute_reply": "2025-02-03T06:25:56.305974Z",
     "shell.execute_reply.started": "2025-02-03T06:25:56.302962Z"
    },
    "jupyter": {
     "outputs_hidden": false
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "import torch.optim as optim"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "jp-MarkdownHeadingCollapsed": true,
    "tags": []
   },
   "source": [
    "## 训练\n",
    "\n",
    "pytorch的训练需要自行实现，包括\n",
    "1. 定义损失函数\n",
    "2. 定义优化器\n",
    "3. 定义训练步\n",
    "4. 训练"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-02-03T06:26:03.070601Z",
     "iopub.status.busy": "2025-02-03T06:26:03.069720Z",
     "iopub.status.idle": "2025-02-03T06:26:03.192118Z",
     "shell.execute_reply": "2025-02-03T06:26:03.191258Z",
     "shell.execute_reply.started": "2025-02-03T06:26:03.070565Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "from sklearn.metrics import accuracy_score\n",
    "\n",
    "@torch.no_grad()\n",
    "def evaluating(model, dataloader, loss_fct):\n",
    "    loss_list = []\n",
    "    pred_list = []\n",
    "    label_list = []\n",
    "    for datas, labels in dataloader:\n",
    "        datas = datas.to(device)\n",
    "        labels = labels.to(device)\n",
    "        # 前向计算\n",
    "        logits = model(datas)\n",
    "        loss = loss_fct(logits, labels)         # 验证集损失\n",
    "        loss_list.append(loss.item())\n",
    "        \n",
    "        preds = logits.argmax(axis=-1)    # 验证集预测\n",
    "        pred_list.extend(preds.cpu().numpy().tolist())\n",
    "        label_list.extend(labels.cpu().numpy().tolist())\n",
    "        \n",
    "    acc = accuracy_score(label_list, pred_list)\n",
    "    return np.mean(loss_list), acc\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "jp-MarkdownHeadingCollapsed": true,
    "tags": []
   },
   "source": [
    "### TensorBoard 可视化\n",
    "\n",
    "\n",
    "训练过程中可以使用如下命令启动tensorboard服务。\n",
    "\n",
    "```shell\n",
    "tensorboard \\\n",
    "    --logdir=runs \\     # log 存放路径\n",
    "    --host 0.0.0.0 \\    # ip\n",
    "    --port 8848         # 端口\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-02-03T06:26:09.028631Z",
     "iopub.status.busy": "2025-02-03T06:26:09.027575Z",
     "iopub.status.idle": "2025-02-03T06:26:09.257008Z",
     "shell.execute_reply": "2025-02-03T06:26:09.256125Z",
     "shell.execute_reply.started": "2025-02-03T06:26:09.028580Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "from torch.utils.tensorboard import SummaryWriter\n",
    "\n",
    "\n",
    "class TensorBoardCallback:\n",
    "    def __init__(self, log_dir, flush_secs=10):\n",
    "        \"\"\"\n",
    "        Args:\n",
    "            log_dir (str): dir to write log.\n",
    "            flush_secs (int, optional): write to dsk each flush_secs seconds. Defaults to 10.\n",
    "        \"\"\"\n",
    "        self.writer = SummaryWriter(log_dir=log_dir, flush_secs=flush_secs)\n",
    "\n",
    "    def draw_model(self, model, input_shape):\n",
    "        self.writer.add_graph(model, input_to_model=torch.randn(input_shape))\n",
    "        \n",
    "    def add_loss_scalars(self, step, loss, val_loss):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/loss\", \n",
    "            tag_scalar_dict={\"loss\": loss, \"val_loss\": val_loss},\n",
    "            global_step=step,\n",
    "            )\n",
    "        \n",
    "    def add_acc_scalars(self, step, acc, val_acc):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/accuracy\",\n",
    "            tag_scalar_dict={\"accuracy\": acc, \"val_accuracy\": val_acc},\n",
    "            global_step=step,\n",
    "        )\n",
    "        \n",
    "    def add_lr_scalars(self, step, learning_rate):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/learning_rate\",\n",
    "            tag_scalar_dict={\"learning_rate\": learning_rate},\n",
    "            global_step=step,\n",
    "            \n",
    "        )\n",
    "    \n",
    "    def __call__(self, step, **kwargs):\n",
    "        # add loss\n",
    "        loss = kwargs.pop(\"loss\", None)\n",
    "        val_loss = kwargs.pop(\"val_loss\", None)\n",
    "        if loss is not None and val_loss is not None:\n",
    "            self.add_loss_scalars(step, loss, val_loss)\n",
    "        # add acc\n",
    "        acc = kwargs.pop(\"acc\", None)\n",
    "        val_acc = kwargs.pop(\"val_acc\", None)\n",
    "        if acc is not None and val_acc is not None:\n",
    "            self.add_acc_scalars(step, acc, val_acc)\n",
    "        # add lr\n",
    "        learning_rate = kwargs.pop(\"lr\", None)\n",
    "        if learning_rate is not None:\n",
    "            self.add_lr_scalars(step, learning_rate)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "jp-MarkdownHeadingCollapsed": true,
    "tags": []
   },
   "source": [
    "### Save Best\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-02-03T06:26:13.177203Z",
     "iopub.status.busy": "2025-02-03T06:26:13.176475Z",
     "iopub.status.idle": "2025-02-03T06:26:13.184742Z",
     "shell.execute_reply": "2025-02-03T06:26:13.183851Z",
     "shell.execute_reply.started": "2025-02-03T06:26:13.177165Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "class SaveCheckpointsCallback:\n",
    "    def __init__(self, save_dir, save_step=5000, save_best_only=True):\n",
    "        \"\"\"\n",
    "        Save checkpoints each save_epoch epoch. \n",
    "        We save checkpoint by epoch in this implementation.\n",
    "        Usually, training scripts with pytorch evaluating model and save checkpoint by step.\n",
    "\n",
    "        Args:\n",
    "            save_dir (str): dir to save checkpoint\n",
    "            save_epoch (int, optional): the frequency to save checkpoint. Defaults to 1.\n",
    "            save_best_only (bool, optional): If True, only save the best model or save each model at every epoch.\n",
    "        \"\"\"\n",
    "        self.save_dir = save_dir\n",
    "        self.save_step = save_step\n",
    "        self.save_best_only = save_best_only\n",
    "        self.best_metrics = -1\n",
    "        \n",
    "        # mkdir\n",
    "        if not os.path.exists(self.save_dir):\n",
    "            os.mkdir(self.save_dir)\n",
    "        \n",
    "    def __call__(self, step, state_dict, metric=None):\n",
    "        if step % self.save_step > 0:\n",
    "            return\n",
    "        \n",
    "        if self.save_best_only:\n",
    "            assert metric is not None\n",
    "            if metric >= self.best_metrics:\n",
    "                # save checkpoints\n",
    "                torch.save(state_dict, os.path.join(self.save_dir, \"best.ckpt\"))\n",
    "                # update best metrics\n",
    "                self.best_metrics = metric\n",
    "        else:\n",
    "            torch.save(state_dict, os.path.join(self.save_dir, f\"{step}.ckpt\"))\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "jp-MarkdownHeadingCollapsed": true,
    "tags": []
   },
   "source": [
    "### Early Stop"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-02-03T06:26:16.158937Z",
     "iopub.status.busy": "2025-02-03T06:26:16.158362Z",
     "iopub.status.idle": "2025-02-03T06:26:16.165345Z",
     "shell.execute_reply": "2025-02-03T06:26:16.164434Z",
     "shell.execute_reply.started": "2025-02-03T06:26:16.158901Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "class EarlyStopCallback:\n",
    "    def __init__(self, patience=5, min_delta=0.01):\n",
    "        \"\"\"\n",
    "\n",
    "        Args:\n",
    "            patience (int, optional): Number of epochs with no improvement after which training will be stopped.. Defaults to 5.\n",
    "            min_delta (float, optional): Minimum change in the monitored quantity to qualify as an improvement, i.e. an absolute \n",
    "                change of less than min_delta, will count as no improvement. Defaults to 0.01.\n",
    "        \"\"\"\n",
    "        self.patience = patience\n",
    "        self.min_delta = min_delta\n",
    "        self.best_metric = -1\n",
    "        self.counter = 0\n",
    "        \n",
    "    def __call__(self, metric):\n",
    "        if metric >= self.best_metric + self.min_delta:\n",
    "            # update best metric\n",
    "            self.best_metric = metric\n",
    "            # reset counter \n",
    "            self.counter = 0\n",
    "        else: \n",
    "            self.counter += 1\n",
    "            \n",
    "    @property\n",
    "    def early_stop(self):\n",
    "        return self.counter >= self.patience\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## training"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-02-03T07:01:16.719018Z",
     "iopub.status.busy": "2025-02-03T07:01:16.718496Z",
     "iopub.status.idle": "2025-02-03T07:04:21.102796Z",
     "shell.execute_reply": "2025-02-03T07:04:21.101710Z",
     "shell.execute_reply.started": "2025-02-03T07:01:16.718983Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      " 75%|███████▌  | 10560/14080 [03:04<01:01, 57.38it/s, epoch=14]"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Early stop at epoch 15 / global_step 10560\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "\n"
     ]
    }
   ],
   "source": [
    "# 训练\n",
    "def training(\n",
    "    model, \n",
    "    train_loader, \n",
    "    val_loader, \n",
    "    epoch, \n",
    "    loss_fct, \n",
    "    optimizer, \n",
    "    tensorboard_callback=None,\n",
    "    save_ckpt_callback=None,\n",
    "    early_stop_callback=None,\n",
    "    eval_step=500,\n",
    "    ):\n",
    "    record_dict = {\n",
    "        \"train\": [],\n",
    "        \"val\": []\n",
    "    }\n",
    "    \n",
    "    global_step = 0\n",
    "    model.train()\n",
    "    with tqdm(total=epoch * len(train_loader)) as pbar:\n",
    "        for epoch_id in range(epoch):\n",
    "            # training\n",
    "            for datas, labels in train_loader:\n",
    "                datas = datas.to(device)\n",
    "                labels = labels.to(device)\n",
    "                # 梯度清空\n",
    "                optimizer.zero_grad()\n",
    "                # 模型前向计算\n",
    "                logits = model(datas)\n",
    "                # 计算损失\n",
    "                loss = loss_fct(logits, labels)\n",
    "                # 梯度回传\n",
    "                loss.backward()\n",
    "                # 调整优化器，包括学习率的变动等\n",
    "                optimizer.step()\n",
    "                preds = logits.argmax(axis=-1)\n",
    "            \n",
    "                acc = accuracy_score(labels.cpu().numpy(), preds.cpu().numpy())    \n",
    "                loss = loss.cpu().item()\n",
    "                # record\n",
    "                \n",
    "                record_dict[\"train\"].append({\n",
    "                    \"loss\": loss, \"acc\": acc, \"step\": global_step\n",
    "                })\n",
    "                \n",
    "                # evaluating\n",
    "                if global_step % eval_step == 0:\n",
    "                    model.eval()\n",
    "                    val_loss, val_acc = evaluating(model, val_loader, loss_fct)\n",
    "                    record_dict[\"val\"].append({\n",
    "                        \"loss\": val_loss, \"acc\": val_acc, \"step\": global_step\n",
    "                    })\n",
    "                    model.train()\n",
    "                    \n",
    "                    # 1. 使用 tensorboard 可视化\n",
    "                    if tensorboard_callback is not None:\n",
    "                        tensorboard_callback(\n",
    "                            global_step, \n",
    "                            loss=loss, val_loss=val_loss,\n",
    "                            acc=acc, val_acc=val_acc,\n",
    "                            lr=optimizer.param_groups[0][\"lr\"],\n",
    "                            )\n",
    "                \n",
    "                    # 2. 保存模型权重 save model checkpoint\n",
    "                    if save_ckpt_callback is not None:\n",
    "                        save_ckpt_callback(global_step, model.state_dict(), metric=val_acc)\n",
    "\n",
    "                    # 3. 早停 Early Stop\n",
    "                    if early_stop_callback is not None:\n",
    "                        early_stop_callback(val_acc)\n",
    "                        if early_stop_callback.early_stop:\n",
    "                            print(f\"Early stop at epoch {epoch_id} / global_step {global_step}\")\n",
    "                            return record_dict\n",
    "                    \n",
    "                # udate step\n",
    "                global_step += 1\n",
    "                pbar.update(1)\n",
    "                pbar.set_postfix({\"epoch\": epoch_id})\n",
    "        \n",
    "    return record_dict\n",
    "        \n",
    "\n",
    "epoch = 20\n",
    "\n",
    "model = InceptionNet(num_classes=10)\n",
    "\n",
    "# 1. 定义损失函数 采用交叉熵损失\n",
    "loss_fct = nn.CrossEntropyLoss()\n",
    "# 2. 定义优化器 采用 Rmsprop\n",
    "# Optimizers specified in the torch.optim package\n",
    "optimizer = torch.optim.RMSprop(model.parameters(), lr=0.001, alpha=0.9, eps=1e-07)\n",
    "\n",
    "# 1. tensorboard 可视化\n",
    "if not os.path.exists(\"runs\"):\n",
    "    os.mkdir(\"runs\")\n",
    "    \n",
    "exp_name = \"inception_net\"\n",
    "tensorboard_callback = TensorBoardCallback(f\"runs/{exp_name}\")\n",
    "tensorboard_callback.draw_model(model, [1, 3, IMAGE_SIZE, IMAGE_SIZE])\n",
    "# 2. save best\n",
    "if not os.path.exists(\"checkpoints\"):\n",
    "    os.makedirs(\"checkpoints\")\n",
    "save_ckpt_callback = SaveCheckpointsCallback(f\"checkpoints/{exp_name}\", save_step=len(train_dl), save_best_only=True)\n",
    "# 3. early stop\n",
    "early_stop_callback = EarlyStopCallback(patience=5)\n",
    "\n",
    "model = model.to(device)\n",
    "record = training(\n",
    "    model, \n",
    "    train_dl, \n",
    "    eval_dl, \n",
    "    epoch, \n",
    "    loss_fct, \n",
    "    optimizer, \n",
    "    tensorboard_callback=tensorboard_callback,\n",
    "    save_ckpt_callback=save_ckpt_callback,\n",
    "    early_stop_callback=early_stop_callback,\n",
    "    eval_step=len(train_dl)\n",
    "    )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-02-03T06:50:45.677160Z",
     "iopub.status.busy": "2025-02-03T06:50:45.676602Z",
     "iopub.status.idle": "2025-02-03T06:50:46.001287Z",
     "shell.execute_reply": "2025-02-03T06:50:46.000347Z",
     "shell.execute_reply.started": "2025-02-03T06:50:45.677119Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 1000x500 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "#画线要注意的是损失是不一定在零到1之间的\n",
    "def plot_learning_curves(record_dict, sample_step=500):\n",
    "    # build DataFrame\n",
    "    train_df = pd.DataFrame(record_dict[\"train\"]).set_index(\"step\").iloc[::sample_step]\n",
    "    val_df = pd.DataFrame(record_dict[\"val\"]).set_index(\"step\")\n",
    "\n",
    "    # plot\n",
    "    fig_num = len(train_df.columns)\n",
    "    fig, axs = plt.subplots(1, fig_num, figsize=(5 * fig_num, 5))\n",
    "    for idx, item in enumerate(train_df.columns):    \n",
    "        axs[idx].plot(train_df.index, train_df[item], label=f\"train_{item}\")\n",
    "        axs[idx].plot(val_df.index, val_df[item], label=f\"val_{item}\")\n",
    "        axs[idx].grid()\n",
    "        axs[idx].legend()\n",
    "        # axs[idx].set_xticks(range(0, train_df.index[-1], 5000))\n",
    "        # axs[idx].set_xticklabels(map(lambda x: f\"{int(x/1000)}k\", range(0, train_df.index[-1], 5000)))\n",
    "        axs[idx].set_xlabel(\"step\")\n",
    "    \n",
    "    plt.show()\n",
    "\n",
    "plot_learning_curves(record, sample_step=100)  #横坐标是 steps"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 评估"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-02-03T06:50:51.249697Z",
     "iopub.status.busy": "2025-02-03T06:50:51.248778Z",
     "iopub.status.idle": "2025-02-03T06:50:52.338616Z",
     "shell.execute_reply": "2025-02-03T06:50:52.337521Z",
     "shell.execute_reply.started": "2025-02-03T06:50:51.249657Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "loss:     0.9008\n",
      "accuracy: 0.6950\n"
     ]
    }
   ],
   "source": [
    "# dataload for evaluating\n",
    "\n",
    "# load checkpoints\n",
    "model.load_state_dict(torch.load(f\"checkpoints/{exp_name}/best.ckpt\", weights_only=True, map_location=\"cpu\"))\n",
    "\n",
    "model.eval()\n",
    "loss, acc = evaluating(model, eval_dl, loss_fct)\n",
    "print(f\"loss:     {loss:.4f}\\naccuracy: {acc:.4f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 推理"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-01-21T08:20:00.806997Z",
     "iopub.status.busy": "2025-01-21T08:20:00.806739Z",
     "iopub.status.idle": "2025-01-21T08:21:58.270987Z",
     "shell.execute_reply": "2025-01-21T08:21:58.270499Z",
     "shell.execute_reply.started": "2025-01-21T08:20:00.806974Z"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 4688/4688 [01:57<00:00, 39.92it/s]\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>filepath</th>\n",
       "      <th>class</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>competitions/cifar-10/test/1.png</td>\n",
       "      <td>dog</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>competitions/cifar-10/test/2.png</td>\n",
       "      <td>airplane</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>competitions/cifar-10/test/3.png</td>\n",
       "      <td>automobile</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>competitions/cifar-10/test/4.png</td>\n",
       "      <td>airplane</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>competitions/cifar-10/test/5.png</td>\n",
       "      <td>airplane</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                           filepath       class\n",
       "0  competitions/cifar-10/test/1.png         dog\n",
       "1  competitions/cifar-10/test/2.png    airplane\n",
       "2  competitions/cifar-10/test/3.png  automobile\n",
       "3  competitions/cifar-10/test/4.png    airplane\n",
       "4  competitions/cifar-10/test/5.png    airplane"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# test_df\n",
    "test_ds = Cifar10Dataset(\"test\", transform=transforms_eval)\n",
    "test_dl = DataLoader(test_ds, batch_size=batch_size, shuffle=False, drop_last=False)\n",
    "\n",
    "preds_collect = []\n",
    "model.eval()\n",
    "for data, fake_label in tqdm(test_dl):\n",
    "    data = data.to(device=device)\n",
    "    logits = model(data)\n",
    "    preds = [test_ds.idx_to_label[idx] for idx in logits.argmax(axis=-1).cpu().tolist()]\n",
    "    preds_collect.extend(preds)\n",
    "    \n",
    "test_df[\"class\"] = preds_collect\n",
    "test_df.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-01-21T08:21:58.272012Z",
     "iopub.status.busy": "2025-01-21T08:21:58.271588Z",
     "iopub.status.idle": "2025-01-21T08:21:58.590533Z",
     "shell.execute_reply": "2025-01-21T08:21:58.589993Z",
     "shell.execute_reply.started": "2025-01-21T08:21:58.271992Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# 导出 submission.csv\n",
    "test_df.to_csv(\"submission.csv\", index=False)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.14"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
